import type { ComputedRef, Ref } from "vue"
import type { BasicTableProps } from "../types/table"
import { computed, unref, ref, toRaw } from "vue"
import { ROW_KEY, FETCH_SETTING } from "../const"
import { isEmpty, isFunction } from "/@/utils/is"

export function useTableExpand(
  propsRef: ComputedRef<BasicTableProps>,
  tableData: Ref<Recordable[]>,
  getFormData: Fn,
  emit: EmitType,
  setLoading: Fn
) {
  const expandedRowKeys = ref<string[]>([])
  const currentLevel = ref(0)

  const getChildrenColumnName = computed(() => {
    const { childrenColumnName } = unref(propsRef)
    return childrenColumnName || "children"
  })

  const getAutoCreateKey = computed(() => {
    return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey
  })

  const getRowKey = computed(() => {
    const { rowKey } = unref(propsRef)
    return unref(getAutoCreateKey) ? ROW_KEY : rowKey
  })

  const getExpandOption = computed(() => {
    const { isTreeTable, isNestedTable } = unref(propsRef)
    if (!isTreeTable && !isNestedTable) return {}


    return {
      expandedRowKeys: unref(expandedRowKeys),
      onExpandedRowsChange: (keys: string[]) => {
        expandedRowKeys.value = keys
        emit("expanded-rows-change", keys)
      },
    }
  })

  function expandAll(isInvokeApi?: true) {
    setLoading(true)
    setTimeout(async () => {
      try {
        if (isInvokeApi && getFormData()) {
          await expandOneLevel()
          currentLevel.value += 1
        } else {
          const keys = getAllKeys()
          expandedRowKeys.value = keys
        }
      } finally {
        setLoading(false)
      }
    })
  }

  function expandRows(keys: string[]) {
    // use row ID expands the specified table row
    const { isTreeTable } = unref(propsRef)
    if (!isTreeTable) return
    expandedRowKeys.value = [...expandedRowKeys.value, ...keys]
  }

  function getAllKeys(data?: Recordable[]) {
    const keys: string[] = []
    toRaw(data || unref(tableData)).forEach((item) => {
      keys.push(item[unref(getRowKey) as string])
      const children = item[unref(getChildrenColumnName)]
      if (children?.length) {
        keys.push(...getAllKeys(children))
      }
    })
    return keys
  }

  async function expandOneLevel(data?: Recordable[], treeLevel = 0) {
    const rowKey = unref(getRowKey) as string
    for (const record of toRaw(data || tableData.value)) {
      await expandCollapse(record, true)
      expandedRowKeys.value.push(record[rowKey])
      if (treeLevel < currentLevel.value) {
        const children = record[unref(getChildrenColumnName)]
        if (children?.length > 0) {
          await expandOneLevel(children, treeLevel + 1)
        }
      }
    }
  }

  function collapseAll() {
    expandedRowKeys.value = []
    currentLevel.value = 0
  }

  async function expandCollapse(record: Recordable, onlyLoadData = false, forceLoad = false) {
    const { api, fetchSetting } = unref(propsRef)
    const { listField } = Object.assign({}, FETCH_SETTING, fetchSetting)
    const treeLeaf = record.treeLeaf && record.treeLeaf === "1"

    if ((!treeLeaf && isEmpty(record[unref(getChildrenColumnName)])) || forceLoad) {
      record.isLoading = true
      if (!api || !isFunction(api)) return
      const rowKey = unref(getRowKey) as string
      const res = await api({
        parentId: record[rowKey] as string,
        status: forceLoad ? "" : getFormData()?.status,
      })

      const isArrayResult = Array.isArray(res)
      const resultItems: Recordable[] = isArrayResult ? res : res[listField]

      if (resultItems && resultItems.length > 0) {
        record.children = resultItems
        record.treeLeaf = "0"
      } else {
        record.children = []
        record.treeLeaf = "1"
      }

      record.isLoading = false
    }
    if (!onlyLoadData) {
      let expanded = true
      const rowKey = unref(getRowKey) as string
      for (const index in expandedRowKeys.value) {
        if (record[rowKey] == expandedRowKeys.value[index]) {
          expandedRowKeys.value.splice(index as unknown as number, 1)
          expanded = false
          break
        }
      }
      if (forceLoad) {
        expanded = true
      }
      if (expanded) {
        expandedRowKeys.value.push(record[unref(getRowKey) as string])
      }
      return expanded
    }
  }

  return { getExpandOption, expandAll, expandRows, collapseAll, expandCollapse }
}
